package com.todo.event.activity.application.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.api.client.util.Lists;
import com.todo.common.common.entity.response.page.PageResult;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.StringUtils;
import com.todo.component.redis.RedisService;
import com.todo.event.activity.application.convert.EventActivityConvert;
import com.todo.event.activity.application.core.engine.aviator.AviatorHelper;
import com.todo.event.activity.application.entity.bo.EventDetailBO;
import com.todo.event.activity.application.entity.bo.EventInfoBO;
import com.todo.event.activity.application.entity.condition.EventInfoConditionBO;
import com.todo.event.activity.application.service.EventDetailService;
import com.todo.event.activity.application.util.EventActivityUtil;
import com.todo.event.activity.common.base.EventKeyBuild;
import com.todo.event.activity.common.enums.EventClassifyEnum;
import com.todo.event.activity.common.enums.EventDetailStatusEnum;
import com.todo.event.activity.common.enums.EventFieldDataSourceEnum;
import com.todo.common.utils.TodoCommonUtils;
import com.todo.event.activity.infrastructure.entity.condition.EventInfoCondition;
import com.todo.event.activity.infrastructure.entity.domain.EventDetailDO;
import com.todo.event.activity.infrastructure.entity.po.EventInfoPO;
import com.todo.event.activity.infrastructure.repository.EventDetailRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author azhebuxing
 * @date 2025/1/7 13:14
 * @description
 */
@Slf4j
@Service
public class EventDetailServiceImpl implements EventDetailService {

    @Resource
    private EventDetailRepository eventDetailRepository;
    @Resource
    private RedisService redisService;

    @Override
    public Boolean insertEventDetail(EventDetailBO eventDetailBO) {
        if (eventDetailBO == null) {
            return false;
        }
        if (eventDetailBO.getEventInfo() == null || eventDetailBO.getEventFieldList() == null) {
            log.error("EventDetailService#insertEventDetail 新增事件详情失败，核心参数为空 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            return false;
        }
        EventDetailBO alreadyEventDetailBO = queryEventDetailByCode(eventDetailBO.getEventCode());
        if (alreadyEventDetailBO != null) {
            log.error("EventDetailService#insertEventDetail 新增事件详情失败，事件编码重复 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.SAME_EVENT_CODE);
        }
        //TODO 当前支持且仅支持MQ消息分类
        if (eventDetailBO.getEventInfo().getEventClassify() != EventClassifyEnum.MQ) {
            log.error("EventDetailService#insertEventDetail 新增事件详情失败，当前仅支持MQ消息分类 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_EVENT_CLASSIFY);
        }
        //通用字段校验
        generalFieldCheck(eventDetailBO);

        //判断是否存在同名数据
        Boolean exist = eventDetailRepository.checkEventNameExist(eventDetailBO.getEventInfo().getEventName());
        if (exist) {
            log.error("EventDetailService#insertEventDetail 新增事件详情失败，当前事件名称已存在 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.SAME_EVENT_NAME);
        }
        eventDetailBO.setStatus(EventDetailStatusEnum.DRAFT);
        EventDetailDO eventDetailDO = EventActivityConvert.convertToEventDetailDO(eventDetailBO);
        Boolean inserted = eventDetailRepository.insertEventDetail(eventDetailDO);
        if (inserted) {
            String eventDetailKey = EventKeyBuild.buildEventDetailKey(eventDetailBO.getEventCode());
            redisService.set8H(eventDetailKey, JSONObject.toJSONString(eventDetailBO));
        }
        return inserted;
    }

    private void generalFieldCheck(EventDetailBO eventDetailBO) {
        List<EventDetailBO.EventField> eventFieldList = eventDetailBO.getEventFieldList();
        if (CollectionUtils.isEmpty(eventFieldList)) {
            log.error("EventDetailService#generalFieldCheck 新增事件详情失败，事件字段列表为空 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        //判断当前字段是否存在多个唯一值或者不存在唯一值，同时用户id必填
        long uniqueCount = eventFieldList.stream().filter(EventDetailBO.EventField::getUniqueField).count();
        if (uniqueCount != 1) {
            log.error("EventDetailService#generalFieldCheck 新增事件详情失败，当前字段不存在唯一值或者存在多个唯一值，默认不允许操作 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_EVENT_FIELD);
        }
        if (eventFieldList.stream().noneMatch(item -> item.getFieldName().equals(EventActivityUtil.EVENT_FIELD_USER_ID_KEY))) {
            log.error("EventDetailService#generalFieldCheck 新增事件详情失败，当前字段不存在userId字段，默认不允许操作 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_EVENT_FIELD);
        }
        for (EventDetailBO.EventField eventField : eventFieldList) {
            //如果是消息体中获取的逻辑，则使用Aviator做逻辑判断
            if (EventFieldDataSourceEnum.AVIATOR.equals(eventField.getDataSource())) {
                boolean validateExp = AviatorHelper.validateExp(eventField.getTransfer());
                if (!validateExp) {
                    log.error("EventDetailService#generalFieldCheck 新增事件详情失败，当前字段消息体中获取的逻辑表达式不合法 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
                    throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "当前字段消息体中获取的逻辑表达式不合法");
                }
            } else if (EventFieldDataSourceEnum.QLEXPRESS.equals(eventField.getDataSource())) {
                //TODO 后续做代码配置脚本使用（可以从各类接口等数据中获取）
            } else if (EventFieldDataSourceEnum.STATIC_PROPERTY.equals(eventField.getDataSource())) {
                //静态数据默认通过
                continue;
            }
        }
    }

    @Override
    public EventDetailBO queryEventDetail(Long eventId) {
        if (eventId == null || eventId <= 0) {
            return null;
        }
        EventDetailDO eventDetailDO = eventDetailRepository.queryEventDetail(eventId);
        return EventActivityConvert.convertToEventDetailBO(eventDetailDO);
    }

    @Override
    public EventDetailBO queryEventDetailByCode(String eventCode) {
        if (StringUtils.isBlank(eventCode)) {
            return null;
        }
        String eventDetailKey = EventKeyBuild.buildEventDetailKey(eventCode);
        if (redisService.hasKey(eventDetailKey)) {
            String eventDetailJson = redisService.get(eventDetailKey);
            return JSONObject.parseObject(eventDetailJson, EventDetailBO.class);
        }
        EventDetailDO eventDetailDO = eventDetailRepository.queryEventDetailByCode(eventCode);
        if (eventDetailDO != null) {
            EventDetailBO eventDetailBO = EventActivityConvert.convertToEventDetailBO(eventDetailDO);
            redisService.set8H(eventDetailKey, JSONObject.toJSONString(eventDetailBO));
            return eventDetailBO;
        }
        log.warn("EventDetailService#queryEventDetailByCode 查询事件详情失败，事件编码不存在 eventCode：【{}】", eventCode);
        return null;
    }

    @Override
    public Boolean updateEventDetail(EventDetailBO eventDetailBO) {
        if (eventDetailBO == null || eventDetailBO.getId() == null) {
            return false;
        }
        if (eventDetailBO.getEventInfo() == null || eventDetailBO.getEventFieldList() == null) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，核心参数为空 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            return false;
        }
        EventDetailBO alreadyEventDetailBO = queryEventDetail(eventDetailBO.getId());
        if (alreadyEventDetailBO == null) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，事件详情不存在 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        if (alreadyEventDetailBO.getStatus().equals(EventDetailStatusEnum.RUNNING)) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，当前事件详情状态无法操作，无法调整 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_OPERATE_STATUS);
        }
        if (!alreadyEventDetailBO.getEventCode().equals(eventDetailBO.getEventCode())) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，事件编码不允许修改 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.ILLEGAL_OPERATION, "事件编码不允许修改");
        }
        //TODO 当前支持且仅支持MQ消息分类
        if (eventDetailBO.getEventInfo().getEventClassify() != EventClassifyEnum.MQ) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，当前仅支持MQ消息分类 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_EVENT_CLASSIFY);
        }
        generalFieldCheck(eventDetailBO);

        //判断是否存在同名数据
        Boolean exist = eventDetailRepository.checkEventNameExist(eventDetailBO.getEventInfo().getEventName());
        if (exist) {
            log.error("EventDetailService#updateEventDetail 更新事件详情失败，当前事件名称已存在 eventDetailBO：【{}】", JSONObject.toJSONString(eventDetailBO));
            throw new BizException(BizErrorCodeEnum.SAME_EVENT_NAME);
        }
        eventDetailBO.setStatus(EventDetailStatusEnum.DRAFT);
        EventDetailDO eventDetailDO = EventActivityConvert.convertToEventDetailDO(eventDetailBO);
        Boolean updated = eventDetailRepository.updateEventDetail(eventDetailDO);
        if (updated) {
            String eventDetailKey = EventKeyBuild.buildEventDetailKey(eventDetailBO.getEventCode());
            redisService.del(eventDetailKey);
        }
        return updated;
    }

    @Override
    public Boolean removeEventDetailById(Long id) {
        if (id == null || id <= 0) {
            return false;
        }
        EventDetailBO eventDetailBO = queryEventDetail(id);
        if (eventDetailBO == null) {
            return true;
        }
        Boolean removed = eventDetailRepository.removeEventDetailById(id);
        if (removed) {
            redisService.del(EventKeyBuild.buildEventDetailKey(eventDetailBO.getEventCode()));
            String eventCodeRelationKey = EventKeyBuild.buildEventCodeRelationKey(eventDetailBO.getEventInfo().getTopicName(), eventDetailBO.getEventInfo().getTag());
            redisService.sRemove(eventCodeRelationKey, eventDetailBO.getEventCode());
        }
        return removed;
    }

    @Override
    public Boolean updateEventDetailStatus(Long id, EventDetailStatusEnum oldStatus, EventDetailStatusEnum newStatus) {
        if (id == null || oldStatus == null || newStatus == null) {
            return false;
        }
        //基础状态流转判断
        if (newStatus.equals(oldStatus)) {
            return true;
        }
        if (newStatus.equals(EventDetailStatusEnum.RUNNING)) {
            if (!oldStatus.equals(EventDetailStatusEnum.AUDIT_SUCCESS)) {
                log.error("EventDetailService#updateEventDetailStatus 更新事件详情状态失败，当前流转状态不一致，不允许操作 oldStatus：【{}】，newStatus：【{}】", oldStatus, newStatus);
                return false;
            }
        }
        if (newStatus.equals(EventDetailStatusEnum.AUDIT_SUCCESS)) {
            if (!oldStatus.equals(EventDetailStatusEnum.DRAFT)) {
                log.error("EventDetailService#updateEventDetailStatus 更新事件详情状态失败，当前流转状态不一致，不允许操作 oldStatus：【{}】，newStatus：【{}】", oldStatus, newStatus);
                return false;
            }
        }
        EventDetailBO eventDetailBO = queryEventDetail(id);
        if (eventDetailBO == null) {
            log.error("EventDetailService#updateEventDetailStatus 更新事件详情状态失败，当前事件详情不存在 id：【{}】", id);
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        if (eventDetailBO.getStatus().equals(newStatus)) {
            return true;
        }
        Boolean updated = eventDetailRepository.updateEventDetailStatus(id, oldStatus, newStatus);
        if (updated) {
            String eventDetailKey = EventKeyBuild.buildEventDetailKey(eventDetailBO.getEventCode());
            redisService.del(eventDetailKey);
            //新状态为运行时，更新事件编码关联
            if (newStatus.equals(EventDetailStatusEnum.RUNNING)){
                String eventCodeRelationKey = EventKeyBuild.buildEventCodeRelationKey(eventDetailBO.getEventInfo().getTopicName(), eventDetailBO.getEventInfo().getTag());
                redisService.sAdd(eventCodeRelationKey, eventDetailBO.getEventCode());
            } else {
                //其他状态删除关联
                String eventCodeRelationKey = EventKeyBuild.buildEventCodeRelationKey(eventDetailBO.getEventInfo().getTopicName(), eventDetailBO.getEventInfo().getTag());
                redisService.sRemove(eventCodeRelationKey, eventDetailBO.getEventCode());
            }
        }
        return updated;
    }

    @Override
    public PageResult<EventInfoBO> queryEventInfoPageByCondition(EventInfoConditionBO eventInfoConditionBO) {
        if (eventInfoConditionBO == null) {
            return new PageResult<>();
        }
        EventInfoCondition eventInfoCondition = EventActivityConvert.convertToEventInfoCondition(eventInfoConditionBO);
        PageResult<EventInfoPO> pageResult = eventDetailRepository.queryEventInfoPageByCondition(eventInfoCondition);
        if (pageResult == null) {
            return PageResult.of(Lists.newArrayList(), eventInfoConditionBO.getPageIndex(), eventInfoConditionBO.getPageSize(), 0L);
        }
        PageResult<EventInfoBO> result = new PageResult<>();
        result.setResults(pageResult.getResults() == null ? null : pageResult.getResults().stream().map(EventActivityConvert::convertToEventInfoBO).collect(Collectors.toList()));
        result.setHasNext(pageResult.isHasNext());
        result.setTotal(pageResult.getTotal());
        result.setPageCount(pageResult.getPageCount());
        result.setPageNum(pageResult.getPageNum());
        result.setCurrentIndex(pageResult.getCurrentIndex());
        result.setPageSize(pageResult.getPageSize());
        return result;
    }

    @Override
    public List<EventDetailBO> queryEventDetailByTopic(String topicName, String tag) {
        if (StringUtils.isBlank(topicName) || StringUtils.isBlank(tag)) {
            return Collections.emptyList();
        }
        String eventCodeRelationKey = EventKeyBuild.buildEventCodeRelationKey(topicName, tag);
        if (redisService.hasKey(eventCodeRelationKey)) {
            Set<String> cacheValue = redisService.sMembers(eventCodeRelationKey);
            List<EventDetailBO> result = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(cacheValue)) {
                for (String eventCode : cacheValue) {
                    EventDetailBO eventDetailBO = queryEventDetailByCode(eventCode);
                    if (eventDetailBO == null) {
                        continue;
                    }
                    if (!eventDetailBO.getStatus().equals(EventDetailStatusEnum.RUNNING)){
                        continue;
                    }
                    result.add(eventDetailBO);
                }
            }
            return result;
        }
        //避免缓存失效，查询数据库重建
        List<EventDetailDO> eventDetailDOList = eventDetailRepository.queryEventDetailByTopic(topicName, tag);
        if (CollectionUtils.isEmpty(eventDetailDOList)){
            return Collections.emptyList();
        }
        for (EventDetailDO eventDetailDO : eventDetailDOList) {
            EventDetailBO eventDetailBO = EventActivityConvert.convertToEventDetailBO(eventDetailDO);
            redisService.sAdd(eventCodeRelationKey, eventDetailBO.getEventCode());
        }
        return eventDetailDOList.stream()
                .map(EventActivityConvert::convertToEventDetailBO)
                .collect(Collectors.toList());
    }
}
